package com.xiaoyu.lanling.widget.flipper

import android.content.Context
import android.database.Observable
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.ViewFlipper
import com.xiaoyu.lanling.R

class ViewFlipper2 : ViewFlipper {
    private var viewAdapter: ViewFlipperAdapter<*>? = null
    private var emptyView: View? = null
    var viewFlippedListener: OnViewFlipListener? = null
    private val adapterObserver = ViewFlipperAdapterObserver()
    private val pendingFinishRunnable = Runnable {
        viewFlippedListener?.onFinish()
    }
    private val resumeFlipRunnable = Runnable {
        showNext()
    }

    constructor(context: Context?) : this(context, null)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        val ta = context?.obtainStyledAttributes(attrs, R.styleable.ViewFlipper2)
        if (ta?.hasValue(R.styleable.ViewFlipper2_empty_layout) == true) {
            val emptyLayoutId = ta.getResourceId(R.styleable.ViewFlipper2_empty_layout, 0)
            emptyView = LayoutInflater.from(context).inflate(emptyLayoutId, null)
        }
        ta?.recycle()
    }

    fun setAdapter(adapter: ViewFlipperAdapter<*>) {
        if (this.viewAdapter == adapter) return
        this.viewAdapter = adapter
        this.viewAdapter?.registerAdapterDataObserver(adapterObserver)
        doOnDataSetChanged()
    }

    private fun doOnDataSetChanged() {
        stopFlipping()
        removeAllViews()
        val adapter = viewAdapter ?: return
        for (i in 0 until adapter.getItemCount()) {
            val itemView = adapter.getItemView(i)
            addView(itemView)
        }
        if (childCount > 0) {
            // just show next instead of shifting to start state
            showNext()
        } else {
            addView(emptyView)
        }
    }

    private fun doOnItemInsert(index: Int) {
        removeEmptyView()
        val itemView = viewAdapter?.getItemView(index) ?: return
        stopFlipping()
        val currIndex = displayedChild
        addView(itemView, currIndex)
        displayedChild = currIndex
        schedule()
        viewFlippedListener?.onViewFlipped(displayedChild)
    }

    private fun removeEmptyView() {
        if (emptyView?.parent == this) {
            removeView(emptyView)
        }
    }

    override fun showNext() {
        super.showNext()
        viewFlippedListener?.onViewFlipped(displayedChild)
        // the last child is showing ,so we delay finish
        schedule()
    }

    private fun schedule() {
        // when schedule finish or schedule  next , remove old callbacks
        removeCallbacks(resumeFlipRunnable)
        removeCallbacks(pendingFinishRunnable)
        if (displayedChild == childCount - 1) {
            removeCallbacks(resumeFlipRunnable)
            val duration = viewAdapter?.getItemDuration(displayedChild) ?: return
            postDelayed(pendingFinishRunnable, duration)
        } else if (displayedChild < childCount) {
            val duration = viewAdapter?.getItemDuration(displayedChild) ?: return
            postDelayed(resumeFlipRunnable, duration)
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        removeCallbacks(resumeFlipRunnable)
        removeCallbacks(pendingFinishRunnable)
    }

    inner class ViewFlipperAdapterObserver {
        fun notifyDataSetChanged() {
            doOnDataSetChanged()
        }

        fun notifyItemInsert(index: Int) {
            doOnItemInsert(index)
        }
    }

    interface OnViewFlipListener {
        fun onViewFlipped(index: Int)
        fun onFinish()
    }

    abstract class ViewFlipperAdapter<T> {
        private var mObservable = ViewFlipperAdapterObservable()

        abstract fun create(model: T): View

        abstract fun getItemCount(): Int

        fun getItemView(position: Int): View {
            return create(getItem(position))
        }

        abstract fun getItemDuration(position: Int): Long

        abstract fun getItem(position: Int): T

        internal fun registerAdapterDataObserver(observer: ViewFlipperAdapterObserver) {
            mObservable.registerObserver(observer)
        }

        internal fun unregisterAdapterDataObserver(observer: ViewFlipperAdapterObserver) {
            mObservable.unregisterObserver(observer)
        }

        fun notifyDataSetChanged() {
            mObservable.notifyDataSetChange()
        }

        fun notifyItemInsert(position: Int) {
            mObservable.notifyItemInsert(position)
        }

        class ViewFlipperAdapterObservable : Observable<ViewFlipperAdapterObserver>() {
            fun notifyDataSetChange() {
                for (observer in mObservers) {
                    observer.notifyDataSetChanged()
                }
            }

            fun notifyItemInsert(position: Int) {
                for (observer in mObservers) {
                    observer.notifyItemInsert(position)
                }
            }
        }
    }
}